#include "synmidi.h"
#include "synmixer.h"

namespace Synth {

unsigned int ReadVarLen(const unsigned char *&src) {
	unsigned int value;
	unsigned char c;
	if ((value = *src++) & 0x80) {
		value &= 0x7F;
		do {
			value = (value << 7) + ((c = *src++) & 0x7F);
		} while (c & 0x80);
	}
	return (value);
}

MIDIPlayer::MIDIPlayer() {
	buffer = new MIDIEvent[1024*1024];
	bufptr = buffer;
}

int __cdecl CompareEvent(const void *v1, const void *v2) {
	const MIDIEvent *ev1 = (const MIDIEvent *)v1, 
		            *ev2 = (const MIDIEvent *)v2;
	if (ev1->time == ev2->time)
		return ev1->type - ev2->type;
	else
		return ev1->time - ev2->time;
}

template <class T> void insertionSort(T a[], size_t size) {
	for (int i = 1; i < size; i++) {
        T value = a[i];
        int j = i - 1;
		while (j >= 0 && value < a[j]) {
            a[j + 1] = a[j];
            j--;
		}
        a[j + 1] = value;
	}
}

void MIDIPlayer::LoadSong(const unsigned char *data) {
	// Okay, let's mash everything back together and sort
	// TODO: Figure out what stuff belongs in the encoder and what belongs here.
	for (int t = 0; t < CC_MAX+ME_MAX; t++) {
		for (int chan = 0; chan < 16; chan++) {
			int count = ReadVarLen(data);
			int accumTime = 0;
			unsigned char curData1 = 0, curData2 = 0;
			for (int i = 0; i < count; i++) {
				bufptr[i].channel = chan;
				int delta = ReadVarLen(data);
				accumTime += delta;
				bufptr[i].time = accumTime;
				bufptr[i].type = t;
			}
			for (int i = 0; i < count; i++) {
				curData1 += *data++;
				bufptr[i].data1 = curData1;
			}
			if (t == ME_KEYDOWN) {
				for (int i = 0; i < count; i++) {
					curData2 += *data++;
					bufptr[i].data2 = curData2;
				}
			}
			bufptr += count;
		}
	}
	insertionSort(buffer, bufptr - buffer);
	bufptr = buffer;// reset for playback
}

void MIDIPlayer::TimeStep(unsigned int newTime) {
	while (bufptr->time < newTime / (50 * INTERNAL_SAMPLE_RATE / 44100)) {
		mixer->DoFakeMIDICmd(bufptr->channel, bufptr->type, bufptr->data1, bufptr->data2);
		bufptr++;
	}
}

}  // namespace
